const express = require('express')
const jwt = require('jsonwebtoken')
const md5 = require('md5')
const cors = require('cors')
const url = require('url')
const fs = require('fs')
const path = require('path')
const { DataTypes, Sequelize } = require('sequelize')
// 导入multer
const multer = require('multer')

// 定义上传的目录
const upload = multer({ dest: 'uploads/' })
const app = express()

// 导入配置
const { tokenSecret, passwordSecret } = require('./config/secret')

// 数据库连接
const sequelize = require('./config/database')

// 创建模型（Model）
const Users = require('./models/Users')
const Categorys = require('./models/Categorys')
const Articles = require('./models/Articles')
const Medias = require('./models/Medias')

// 同步到数据库
sequelize
  .sync({ alter: true })
  .then(() => {
    console.log('数据库同步成功')
  })
  .catch((err) => {
    console.error('数据库同步失败:', err)
  })

app.use(express.json())
app.use(express.urlencoded({ extended: true }))

// 托管静态资源
app.use('/uploads', express.static('./uploads'))

app.use(cors())

// 编写中间件，验证token
app.use((req, res, next) => {
  const noNeedToken = [
    '/login',
    '/refreshtoken',
    '/getmedia',
    '/delmedia',
    '/upload',
    '/addmedia',
  ]
  // 所有请求都会经过此中间件
  let { pathname } = url.parse(req.url)
  pathname = pathname.toLowerCase()
  if (noNeedToken.includes(pathname)) {
    // 不需要校验，则放行
    next()
  } else {
    // 校验token
    // 1. 获取token
    const token = req.headers.token
    // 2. 解密token,获取用户信息
    try {
      let userInfo = jwt.verify(token, tokenSecret)
      // 给req请求对象添加一个属性，便于下一个中间件获取
      req.userInfo = userInfo
      next()
    } catch (err) {
      // 签名失败（token失效：1. 篡改了token 2. token过期了）
      // http状态码： 401: 用户没有权限  403：用户有权限访问，但是权限不够
      res.status(401).json({ code: 20002, message: '用户权限失败' })
    }
  }
})

// 上传文件
app.post('/upload', upload.single('file'), (req, res) => {
  // 判断是否有文件上传
  if (req.file) {
    // 进行文件的重命名即可 fs.rename
    let { originalname, destination, filename } = req.file
    let dotIndex = originalname.lastIndexOf('.')
    let ext = originalname.substring(dotIndex)
    let oldPath = `${destination}${filename}`
    let newPath = `${destination}${filename}${ext}`
    fs.rename(oldPath, newPath, async (err) => {
      if (err) {
        throw err
      }
      // 将图片插入到附件表
      await Medias.create({ path_url: newPath })
      res.json({ message: '上传文件成功', path: newPath, code: 20000 })
    })
  } else {
    res.json({ message: '没有上传文件', code: 20001 })
  }
})

// 刷新token
app.post('/refreshToken', (req, res) => {
  // 1. 接收刷新refreshToken
  let { refreshToken } = req.body
  // 2. 解析refreshToken，获取其中原来的用户信息，在生成一个新token
  try {
    // 解析refreshToken成功
    let data = jwt.verify(refreshToken, tokenSecret)
    let userInfo = { id: data.id, username: data.username }
    let token = jwt.sign(userInfo, tokenSecret, { expiresIn: 10 }) // 10s
    // 3. 将token响应给前端
    res.json({ token, code: 20000 })
  } catch (err) {
    // 解析refreshToken失败
    res.json({ code: 20004, message: '权限失败' })
  }
})

// 登录接口
app.post('/login', async (req, res) => {
  let { username, password } = req.body
  // 密码加密之后再去查询
  password = md5(password + passwordSecret)
  // 1. 数据库匹配
  let data = await Users.findOne({ where: { username, password } })
  if (data) {
    // 匹配成功，生成token返回给前端
    let userInfo = { id: data.id, username: data.username }
    let token = jwt.sign(userInfo, tokenSecret, { expiresIn: '10h' }) // 10小时
    let refreshToken = jwt.sign(userInfo, tokenSecret, { expiresIn: '15d' }) // 15天
    res.json({ code: 20000, data: { token, refreshToken }, message: 'ok' })
  } else {
    // 匹配失败，提示用户返回给前端
    res.json({ code: 20001, data: {}, message: '用户名或密码失败' })
  }
})

// 获取用户信息
app.post('/getUserInfo', async (req, res) => {
  let { id } = req.userInfo
  // 通过id查询用户信息
  let userInfo = await Users.findByPk(id)
  // 将数据响应给前端
  res.json({
    code: 20000,
    data: { id: userInfo.id, username: userInfo.username },
  })
})

// 获取分类
app.get('/getcate', async (req, res) => {
  let datas = await Categorys.findAll()
  res.json(datas)
})

// 获取文章
app.get('/getarticle', async (req, res) => {
  console.log(req.query)
  let { page = 1, pagesize = 10, input = '' } = req.query
  let offset = (page - 1) * pagesize
  // 1. 查询总记录数
  let sql1 = `SELECT count(id) as count FROM articles where title like '%${input}%'`
  // 2. 查询分页的数据
  let sql2 = `select  t1.*,t2.cate_name,t3.path_url from articles t1  
              left join categorys t2 on t1.cate_id = t2.id 
              left join medias t3 on t1.cover_id = t3.id 
              where t1.title like '%${input}%' 
              order by t1.id desc 
              limit ${offset},${pagesize} 
              `
  let countData = await sequelize.query(sql1, {
    type: Sequelize.QueryTypes.SELECT,
  })

  let resultData = await sequelize.query(sql2, {
    type: Sequelize.QueryTypes.SELECT,
  })

  res.json({ count: countData[0].count, data: resultData })
})

// 添加文章
app.post('/addarticle', async (req, res) => {
  let { cate_id, content, title, cover_id } = req.body
  // 入库
  await Articles.create({ cate_id, content, title, cover_id })
  res.json({ code: 20000, message: '添加文章成功' })
})

// 获取附件
app.get('/getmedia', async (req, res) => {
  let datas = await Medias.findAll()
  res.json(datas)
})

// 删除附件
app.delete('/delmedia', async (req, res) => {
  let { id } = req.query
  let data = await Medias.findByPk(id)
  await Medias.destroy({ where: { id } })
  // 删除物理磁盘中的文件
  if (data.path_url) {
    // 获取文件的绝对路径
    let absUrl = path.join(__dirname, data.path_url)
    fs.unlinkSync(absUrl)
  }
  res.json({ code: 20000, message: '删除成功' })
})

// 添加附件
app.post('/addmedia', async (req, res) => {
  let { path } = req.body
  // 入库
  await Medias.create({ path_url: path })
  res.json({ code: 20000, message: '添加附件成功' })
})

// 获取分类下面文章的总记录数
app.get('/articlecount', async (req, res) => {
  let sql = `SELECT
      count(t1.id) total,
      t2.cate_name 
    FROM
      articles t1
    LEFT JOIN categorys t2 ON t1.cate_id = t2.id
    GROUP BY
      t1.cate_id`
  let datas = await sequelize.query(sql, {
    type: Sequelize.QueryTypes.SELECT,
  })
  res.json(datas)
})

app.get('/test', (req, res) => {
  res.json({ message: 'test' })
})

app.listen(3000, () => {
  console.log('server is running at port 3000')
})
